/*
  Copyright (c) 2011, Mikheev Rostislav
  Copyright (c) 2011, FreshFarsh team

  Steel Engine source code is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License
  as published by the Free Software Foundation; either version 2
  of the License, or (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
/**
  @author Mikheev Rostislav <hacenator@gmail.com>
  @date 23.03.2011
  @brief Configurator class header file
*/

#ifndef STEEL_CONFIG_H_
#define STEEL_CONFIG_H_

#include <list>
#include <stdint.h>
#include <google/dense_hash_map>
#include "common.h"
#include "basic_class.h"

namespace _STEEL_NAMESPACE {

/// Hash function
struct Hash {
  bool operator()(const char *str) const {
    const char *key = str;
    int len = strlen(str);
    uint16_t seed = 1;

    // MurmurHash2, by Austin Appleby
    // 'm' and 'r' are mixing constants generated offline.
    // They're not really 'magic', they just happen to work well.
    const uint16_t m = 0x5bd1e995;
    const int r = 24;

    // Initialize the hash to a 'random' value

    uint32_t h = seed ^ len;

    // Mix 4 bytes at a time into the hash

    const uint8_t *data = (const uint8_t *)key;

    while(len >= 4) {
      uint32_t k = *(uint32_t *)data;

      k *= m;
      k ^= k >> r;
      k *= m;

      h *= m;
      h ^= k;

      data += 4;
      len -= 4;
    }

    // Handle the last few bytes of the input array
    switch(len) {
      case 3: h ^= data[2] << 16;
      case 2: h ^= data[1] << 8;
      case 1: h ^= data[0];
      h *= m;
    };

    // Do a few final mixes of the hash to ensure the last few
    // bytes are well-incorporated.
    h ^= h >> 13;
    h *= m;
    h ^= h >> 15;

    return h;
  }
};

///  EqualKey function
struct EqualString {
  bool operator()(const char *s1, const char *s2) const {
    return (s1 == s2) || (s1 && s2 && strcmp(s1, s2) == 0);
  }
};

class Config: public BasicClass {
 public:
	static Config *GetInstance();
  virtual ~Config();

  //const char & operator[](key_type& key); // :( Now i dont know how to make this overloading

  // get group
  const char *GetString(const char *key);
  int GetInteger(const char *key);
  float GetFloat(const char *key);

  // getDefault group
  const char *GetDefaultString(const char *key, const char *defValue);
  int GetDefaultInteger(const char *key, int default_value);
  float GetDefaultFloat(const char *key, float default_value);

  // set group
  void SetString(const char *key, const char *value);
  void SetInteger(const char *key, int value);
  void SetFloat(const char *key, float value);

  void Clear();
 private:
  Config();

  static Config *_instance; ///< singleton jConfig class instance

  google::dense_hash_map<const char*, const char*, Hash, EqualString> _variables; ///< variables map
  std::list<const char*> _values; ///< variables values list

  uint16_t _max_int;
  uint16_t _max_float;
};

/* python binding */
#define PYTHON_BINDING_CONFIG
struct PythonBinding_Config {
    const char *GetString(const char *key) {
      return Config::GetInstance()->GetString(key);
    };
    int GetInteger(const char *key) {
      return Config::GetInstance()->GetInteger(key);
    };
    float GetFloat(const char *key) {
      return Config::GetInstance()->GetFloat(key);
    };

    const char * GetDefaultString(const char *key, const char *default_value) {
      return Config::GetInstance()->GetDefaultString(key, default_value);
    };
    int GetDefaultInteger(const char *key, int default_value) {
      return Config::GetInstance()->GetDefaultInteger(key, default_value);
    };
    float GetDefaultFloat(const char *key, float default_value) {
      return Config::GetInstance()->GetDefaultFloat(key, default_value);
    };

    void SetString(const char *key, const char *value) {
      Config::GetInstance()->SetString(key, value);
    };
    void SetInteger(const char *key, int value) {
      Config::GetInstance()->SetInteger(key, value);
    };
    void SetFloat(const char *key, float value) {
      Config::GetInstance()->SetFloat(key, value);
    };

    void Clear() {
      Config::GetInstance()->Clear();
    };
};

}

#endif // STEEL_CONFIG_H_
